//==================================================================
//  Copyright (C) 2006-2007  Davide Pasca
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//==================================================================
///
///
///
///
//==================================================================

#include "stdafx.h"
#include "gxy.h"
#include "gxy_base.h"
#include "rend_base.h"

//==================================================================
using namespace PMath;

//==================================================================
namespace GXY {

//#define SPRITE_INT_COORDS

//==================================================================
void SelectTexture( TextureBase *texturep )
{
	_ctxp->_cur_texturep = texturep;
}

//==================================================================
void PrimBegin( PrimType prim_type )
{
	PASSERT( prim_type >= PRIM_QUADS && prim_type < PRIM_TYPES_N );

	if NOT( _ctxp->_n_lists < MAX_SUBLISTS )
	{
		PASSERT( 0 );
		return;
	}

	if ( _ctxp->_n_lists > 0 )
	{
		const sublist_t	*prevlistp = &_ctxp->_lists[ _ctxp->_n_lists-1 ];

		if (	prevlistp->prim_type == prim_type &&
				prevlistp->texturep == _ctxp->_cur_texturep &&
				prevlistp->_blendtype == _ctxp->_sys_state._blend )
		{
			// keep same sub-display-list !
			return;
		}
	}

	// create a new sub-display-list
	_ctxp->_listp = &_ctxp->_lists[ _ctxp->_n_lists++ ];
	_ctxp->_listp->prim_type = (short)prim_type;
	_ctxp->_listp->vcount = 0;
	_ctxp->_listp->vptr = NULL;
	_ctxp->_listp->_blendtype = _ctxp->_sys_state._blend;
	_ctxp->_listp->texturep = _ctxp->_cur_texturep;
	_ctxp->_has_texture = (_ctxp->_cur_texturep != NULL);

	if ( _ctxp->_has_texture )
		_ctxp->_listp->vptr = (void *)&_ctxp->_tex_verts[ _ctxp->_n_tex_verts ];
	else
		_ctxp->_listp->vptr = (void *)&_ctxp->_col_verts[ _ctxp->_n_col_verts ];
}

//==================================================================
void PrimEnd()
{
	const sublist_t	*listp = _ctxp->_listp;

	PASSERT( listp != 0 );
	switch ( listp->prim_type )
	{
	case PRIM_QUADS:		PASSERT( listp->vcount >= 4 && (listp->vcount & 3) == 0 ); break;	// ensures that verts are multiple of 4
	case PRIM_TRIGS:		PASSERT( listp->vcount >= 3 && (listp->vcount % 3) == 0 ); break;	// ensures that verts are multiple of 3
	case PRIM_TRIFAN:		PASSERT( listp->vcount >= 3 ); break;	// ensures that there is at least the stating triangle
	case PRIM_TRISTRIP:		PASSERT( listp->vcount >= 3 ); break;	// ensures that there is at least the stating triangle
	case PRIM_LINES:		PASSERT( listp->vcount >= 2 && (listp->vcount & 1) == 0 ); break;	// ensures that verts are multiple of 2
	case PRIM_LINESTRIP:	PASSERT( listp->vcount >= 2 ); break;
	default:
		PASSERT( 0 );	// paranoia
		break;
	}
}

//==================================================================
void SetVertTxcoord( float s, float t )
{
	_ctxp->_cur_tex[0] = s;
	_ctxp->_cur_tex[1] = t;
}
//==================================================================
void SetVertTxcoord( const float *stp )
{
	_ctxp->_cur_tex[0] = stp[0];
	_ctxp->_cur_tex[1] = stp[1];
}

//==================================================================
void SetVertPos( float x, float y )
{
	PASSERT( _ctxp->_listp != 0 );
	_ctxp->_listp->vcount += 1;

	// could actually put all this into the transformation matrix
	x = (x - _ctxp->_sys_state._viewport[2]*0.5f) * _ctxp->_sys_state._global_sca[0] + _ctxp->_sys_state._global_off[0];
	y = (y - _ctxp->_sys_state._viewport[3]*0.5f) * _ctxp->_sys_state._global_sca[1] + _ctxp->_sys_state._global_off[1];

	int	alpha = (_ctxp->_sys_state._global_alpha_coe * _ctxp->_glt_cur_col[3]) >> 8;
	PCLAMP( alpha, 0, 255 );

	if ( _ctxp->_has_texture )
	{
		if NOT( _ctxp->_n_tex_verts < MAX_VERTS_PER_TYPE )
		{
			PASSERT( 0 );
			return;
		}
		tex_vert_t	*tex_vp = &_ctxp->_tex_verts[ _ctxp->_n_tex_verts++ ];

		tex_vp->pos[0] = x - 0.5f;
		tex_vp->pos[1] = y - 0.5f;
		tex_vp->pos[2] = 0;

		tex_vp->col[0] = _ctxp->_glt_cur_col[0];
		tex_vp->col[1] = _ctxp->_glt_cur_col[1];
		tex_vp->col[2] = _ctxp->_glt_cur_col[2];
		tex_vp->col[3] = alpha;

		tex_vp->tex[0] = _ctxp->_cur_tex[0];
		tex_vp->tex[1] = _ctxp->_cur_tex[1];
	}
	else
	{
		if NOT( _ctxp->_n_col_verts < MAX_VERTS_PER_TYPE )
		{
			PASSERT( 0 );
			return;
		}
		col_vert_t	*col_vp = &_ctxp->_col_verts[ _ctxp->_n_col_verts++ ];

		col_vp->pos[0] = x - 0.5f;
		col_vp->pos[1] = y - 0.5f;
		col_vp->pos[2] = 0;

		col_vp->col[0] = _ctxp->_glt_cur_col[0];
		col_vp->col[1] = _ctxp->_glt_cur_col[1];
		col_vp->col[2] = _ctxp->_glt_cur_col[2];
		col_vp->col[3] = alpha;
	}
}
//==================================================================
void SetVertPos( const float *xyp )
{
	SetVertPos( xyp[0], xyp[1] );
}

//==================================================================
static void setRenderStates( sublist_t *sublistp )
{
	RendDevice	*rdevicep = _ctxp->_rdevicep;

	if NOT( sublistp )
	{
		rdevicep->_DrawState.Reset();
		rdevicep->_DrawState.SetBlending( false );
		rdevicep->_DrawState.SetZWrite( false );
		rdevicep->_DrawState.SetZTest( false );
		rdevicep->_DrawState.SetCulling( PSYSGFX::CULL_OFF );
		rdevicep->_ShaderMng.ResetState();
		return;
	}

	if ( sublistp->_blendtype == PSYSGFX::BASICBLEND_OPAQUE )
	{
		rdevicep->_DrawState.SetBlending( false );
		rdevicep->_DrawState.SetAlphaTest( false );
	}
	else
	{
		switch ( sublistp->_blendtype )
		{
		case PSYSGFX::BASICBLEND_TRANSPARENT:
			rdevicep->_DrawState.SetAlphaTest(	true, PSYSGFX::TESTFUNC_GREATER, 0.1f );
			rdevicep->_DrawState.SetBlending(	true,
											PSYSGFX::BLENDFCT_SRC_ALPHA,
											PSYSGFX::BLENDFCT_ONE_MINUS_SRC_ALPHA );
			break;

		case PSYSGFX::BASICBLEND_ADDITIVE:
			rdevicep->_DrawState.SetAlphaTest( false );
			rdevicep->_DrawState.SetBlending(	true,
											PSYSGFX::BLENDFCT_ONE,
											PSYSGFX::BLENDFCT_ONE );
			break;

		case PSYSGFX::BASICBLEND_SUBTRACTIVE:
			rdevicep->_DrawState.SetAlphaTest( false );
			rdevicep->_DrawState.SetBlending(	true,
											PSYSGFX::BLENDFCT_ZERO,
											PSYSGFX::BLENDFCT_ONE_MINUS_SRC_COLOR );
			break;

		default:
			PASSERT( 0 );
			break;
		}
	}

	//rdevicep->_state.SetTexture( sublistp->texturep );

	rdevicep->_ShaderMng._Std.SetDiffuseTexture( sublistp->texturep );

	rdevicep->_ShaderMng._Std.SetBaseColor( Vector4(1,1,1, _ctxp->_sys_state._global_alpha_coe*(1.0f/256) ),
											 Vector3(0,0,0), 0,
											 Vector4(0,0,0,0) );

	rdevicep->_ShaderMng._Std.SetFogColor( Vector3( 1, 0, 1 ) );

	rdevicep->_ShaderMng._Std.SelectStdVDef( (sublistp->texturep != NULL), true, false );
	rdevicep->_ShaderMng._Std.SelectShader( (sublistp->texturep != NULL), true, false );
}

#if defined(TARGET_D3D)
//==================================================================
static inline D3DPRIMITIVETYPE get_d3d_prim( int prim_type )
{
	switch ( prim_type )
	{
	case PRIM_QUADS:		PASSERTMSG( 0, "no quads !" ); return D3DPT_TRIANGLELIST;
	case PRIM_TRIGS:		return D3DPT_TRIANGLELIST;
	case PRIM_TRIFAN:		return D3DPT_TRIANGLEFAN;
	case PRIM_TRISTRIP:		return D3DPT_TRIANGLESTRIP;
	case PRIM_LINES:		return D3DPT_LINELIST;
	case PRIM_LINESTRIP:	return D3DPT_LINESTRIP;
	default:
		PASSERT( 0 );
		return D3DPT_TRIANGLELIST;
	}
}

//==================================================================
__inline static int get_d3d_prim_count( D3DPRIMITIVETYPE prim_type, int n_verts )
{
	switch ( prim_type )
	{
	case D3DPT_TRIANGLESTRIP:	return (n_verts-2);
	case D3DPT_TRIANGLEFAN:		return (n_verts-2);
	case D3DPT_TRIANGLELIST:	return n_verts / 3;
	case D3DPT_LINELIST:		return n_verts / 2;
	case D3DPT_LINESTRIP:		return (n_verts-1) / 2;
	}

	PASSERT( 0 );
	return 0;
}

#elif defined(TARGET_OPENGL)
//==================================================================
static inline u_int getOGLPrim( int prim_type )
{
	switch ( prim_type )
	{
	case PRIM_QUADS:		return GL_QUADS;
	case PRIM_TRIGS:		return GL_TRIANGLES;
	case PRIM_TRIFAN:		return GL_TRIANGLE_FAN;
	case PRIM_TRISTRIP:		return GL_TRIANGLE_STRIP;
	case PRIM_LINES:		return GL_LINES;
	case PRIM_LINESTRIP:	return GL_LINE_STRIP;
	default:
		PASSERT( 0 );
		return GL_TRIANGLES;
	}
}
#endif

//==================================================================
static void makeOrthogonalProj( Matrix44 &m, float x1, float x2, float y2, float y1, float n, float f )
{
	float	dx, dy, dz;

	m.Identity();

	dx = x2 - x1;
	dy = y1 - y2;
	dz = f - n;

	if ERR_FALSE( dx != 0 && dy != 0 && dz != 0 )
		return;
#ifdef TARGET_D3D
	m._m11 =  2.0f / dx;
	m._m22 =  2.0f / dy;
	m._m33 = -2.0f / dz;
	m._m44 = 1;
	m._m41 = -(x2 + x1) / dx;
	m._m42 = -(y1 + y2) / dy;
	m._m43 = -( 0*f +  n) / dz;
	
#elif defined(TARGET_OPENGL)
	m._m11 =  2.0f / dx;
	m._m22 =  2.0f / dy;
	m._m33 = -2.0f / dz;
	m._m44 = 1;
	m._m41 = -(x2 + x1) / dx;
	m._m42 = -(y1 + y2) / dy;
	m._m43 = -( f +  n) / dz;
	
#endif
}

//==================================================================
static void set_trans_matrix()
{
	int	w = _ctxp->_sys_state._disp_w;
	int	h = _ctxp->_sys_state._disp_h;

#ifdef TARGET_D3D
	XMMATRIX proj_mat;

	if ( w > 0 && h > 0 )
	{
		proj_mat = XMMatrixOrthographicRH( w, h, 0, 1 );

		proj_mat = XMMatrixTranslation( -w*0.5f, h*0.5f, 0 ) * proj_mat;
		proj_mat = XMMatrixScaling( 1, -1, 1 ) * proj_mat;
	}
	else
	{
		proj_mat = XMMatrixIdentity();
	}

	_ctxp->_rdevicep->_ShaderMng.SetProjMatrix( *(Matrix44 *)&proj_mat );

#elif defined(TARGET_OPENGL)
	Matrix44	m;

	if ( w > 0 && h > 0 )
	{
		makeOrthogonalProj( m, 0, w, h, 0, 0, 1 );
	}
	else
		m.Identity();

	_ctxp->_rdevicep->_ShaderMng.SetProjMatrix( m );

#endif


	Matrix44	mview;
	mview.Identity();


	RendShaderManager	&ShaderManager = _ctxp->_rdevicep->_ShaderMng;

	ShaderManager.SetModelviewMatrix0( mview );
	
	ShaderManager.LoadMainMatrices(
		ShaderManager._Std._VSP_U_proj_obj_m,
		ShaderManager._Std._VSP_U_view_obj_m );
}

//==================================================================
void Flush()
{
	/*
	_ctxp->_n_lists = 0;
	_ctxp->_n_col_verts = 0;
	_ctxp->_n_tex_verts = 0;
	return;
	*/

	set_trans_matrix();

	setRenderStates( NULL );

#ifdef TARGET_D3D
	D3DDevice	*d3ddevp = _ctxp->_rdevicep->GetD3DDevice();
#endif

	sublist_t	*listp		= _ctxp->_lists;
	sublist_t	*listp_end	= listp + _ctxp->_n_lists;
	for (; listp != listp_end; ++listp)
	{
		int vtx_size;
		// detect if it's a vertex with texture !
		if ( listp->vptr >= &_ctxp->_tex_verts[0] &&
			 listp->vptr < &_ctxp->_tex_verts[0] + sizeof(_ctxp->_tex_verts)/sizeof(_ctxp->_tex_verts[0]) )
		{
			vtx_size = sizeof(_ctxp->_tex_verts[0]);

			_ctxp->_rdevicep->_ShaderMng.SelectVertexStream( listp->vptr,
					(void *)((u_char *)listp->vptr + sizeof(float) * 3),
					(void *)((u_char *)listp->vptr + sizeof(float) * 3 + sizeof(float) * 2),
					NULL,
					vtx_size );
		}
		else
		{
			vtx_size = sizeof(_ctxp->_col_verts[0]);

			_ctxp->_rdevicep->_ShaderMng.SelectVertexStream( listp->vptr,
				NULL,
				(void *)((u_char *)listp->vptr + sizeof(float) * 3),
				NULL,
				vtx_size );
		}

		setRenderStates( listp );

#ifdef TARGET_D3D
		D3DPRIMITIVETYPE	d3d_prim_type	= get_d3d_prim( listp->prim_type );
		u_int				d3d_prim_count	= get_d3d_prim_count( d3d_prim_type, listp->vcount );

		d3ddevp->DrawPrimitiveUP( d3d_prim_type, d3d_prim_count, listp->vptr, vtx_size );

#elif defined(TARGET_OPENGL)
		glLockArraysEXT( 0, listp->vcount );	// lock arrays for speed

			glDrawArrays( getOGLPrim( listp->prim_type ), 0, listp->vcount );

		glUnlockArraysEXT();	// unlock the arrays
#endif
	}

	_ctxp->_n_lists = 0;
	_ctxp->_n_col_verts = 0;
	_ctxp->_n_tex_verts = 0;
}


//==================================================================
// high-level draw functions
//==================================================================

//==================================================================
void SetScale( float sx, float sy )
{
	_ctxp->_sys_state._sca_x = sx;
	_ctxp->_sys_state._sca_y = sy;
}

//==================================================================
void SetRotation( float angle )
{
	_ctxp->_sys_state._rot_z = angle;
	angle *= 3.14159265 / 180;
	_ctxp->_sys_state._rot_z_cos = cosf( angle );
	_ctxp->_sys_state._rot_z_sin = sinf( angle );
}

//==================================================================
inline void SetColorRGBA( u_char r0, u_char g0, u_char b0, u_char a0,
						  u_char r1, u_char g1, u_char b1, u_char a1,
						  u_char r2, u_char g2, u_char b2, u_char a2,
						  u_char r3, u_char g3, u_char b3, u_char a3 )
{
	u_char	(*colp)[4] = _ctxp->_sys_state._colverts;
	colp[0][0] = r0; colp[0][1] = g0; colp[0][2] = b0; colp[0][3] = a0;
	colp[1][0] = r1; colp[1][1] = g1; colp[1][2] = b1; colp[1][3] = a1;
	colp[2][0] = r2; colp[2][1] = g2; colp[2][2] = b2; colp[2][3] = a2;
	colp[3][0] = r3; colp[3][1] = g3; colp[3][2] = b3; colp[3][3] = a3;
}
//==================================================================
void SetColorRGBA( u_char r0, u_char g0, u_char b0, u_char a0 )
{
	SetColorRGBA( r0, g0, b0, a0,
				  r0, g0, b0, a0, r0, g0, b0, a0, r0, g0, b0, a0 );
}
//==================================================================
void SetColorRGBA( u_char r0, u_char g0, u_char b0, u_char a0,
				   u_char r1, u_char g1, u_char b1, u_char a1 )
{
	SetColorRGBA( r0, g0, b0, a0, r1, g1, b1, a1,
				  r1, g1, b1, a1, r1, g1, b1, a1 );
}
//==================================================================
void SetColorRGBA( u_char r0, u_char g0, u_char b0, u_char a0,
				   u_char r1, u_char g1, u_char b1, u_char a1,
				   u_char r2, u_char g2, u_char b2, u_char a2 )
{
	SetColorRGBA( r0, g0, b0, a0, r1, g1, b1, a1, r2, g2, b2, a2,
				  r2, g2, b2, a2 );
}

//==================================================================
void SetColorRGBA( const u_char *rgba0p,
				   const u_char *rgba1p,
				   const u_char *rgba2p,
				   const u_char *rgba3p )
{
const	u_char	*listp[4] = { rgba0p, rgba1p, rgba2p, rgba3p };
static u_char	basecol[] = { 255, 255, 255, 255 };
const	u_char	*p = basecol;

	u_char	(*colp)[4] = _ctxp->_sys_state._colverts;
	for (int i=0; i < 4; ++i)
	{
		if ( listp[i] )
			p = listp[i];

		colp[i][0] = p[0];
		colp[i][1] = p[1];
		colp[i][2] = p[2];
		colp[i][3] = p[3];
	}
}
//==================================================================
void SetColorRGB( u_char r0, u_char g0, u_char b0 )
{
	SetColorRGBA( r0, g0, b0, 255 );
}
//==================================================================
void SetColorRGB( u_char r0, u_char g0, u_char b0,
				  u_char r1, u_char g1, u_char b1 )
{
	SetColorRGBA( r0, g0, b0, 255, r1, g1, b1, 255 );
}
//==================================================================
void SetColorRGB( u_char r0, u_char g0, u_char b0,
				  u_char r1, u_char g1, u_char b1,
				  u_char r2, u_char g2, u_char b2 )
{
	SetColorRGBA( r0, g0, b0, 255, r1, g1, b1, 255, r2, g2, b2, 255 );
}
//==================================================================
void SetColorRGB( u_char r0, u_char g0, u_char b0,
				  u_char r1, u_char g1, u_char b1,
				  u_char r2, u_char g2, u_char b2,
				  u_char r3, u_char g3, u_char b3 )
{
	SetColorRGBA( r0, g0, b0, 255, r1, g1, b1, 255, r2, g2, b2, 255, r3, g3, b3, 255 );
}

//==================================================================
void SetColorRGB( const u_char *rgb0p,
				  const u_char *rgb1p,
				  const u_char *rgb2p,
				  const u_char *rgb3p )
{
const	u_char	*listp[4] = { rgb0p, rgb1p, rgb2p, rgb3p };
static u_char	basecol[] = { 255, 255, 255, 255 };
const	u_char	*p = basecol;

	u_char	(*colp)[4] = _ctxp->_sys_state._colverts;
	for (int i=0; i < 4; ++i)
	{
		if ( listp[i] )
			p = listp[i];

		colp[i][0] = p[0];
		colp[i][1] = p[1];
		colp[i][2] = p[2];
		colp[i][3] = 255;
	}
}

//==================================================================
//==================================================================
inline void SetOutColorRGBA( u_char r0, u_char g0, u_char b0, u_char a0,
				   u_char r1, u_char g1, u_char b1, u_char a1,
				   u_char r2, u_char g2, u_char b2, u_char a2,
				   u_char r3, u_char g3, u_char b3, u_char a3 )
{
	u_char	(*colp)[4] = _ctxp->_sys_state._outcolverts;
	colp[0][0] = r0; colp[0][1] = g0; colp[0][2] = b0; colp[0][3] = a0;
	colp[1][0] = r1; colp[1][1] = g1; colp[1][2] = b1; colp[1][3] = a1;
	colp[2][0] = r2; colp[2][1] = g2; colp[2][2] = b2; colp[2][3] = a2;
	colp[3][0] = r3; colp[3][1] = g3; colp[3][2] = b3; colp[3][3] = a3;
}
//==================================================================
void SetOutColorRGBA( u_char r0, u_char g0, u_char b0, u_char a0 )
{
	SetOutColorRGBA( r0, g0, b0, a0,
					r0, g0, b0, a0, r0, g0, b0, a0, r0, g0, b0, a0 );
}
//==================================================================
void SetOutColorRGBA( u_char r0, u_char g0, u_char b0, u_char a0,
				   u_char r1, u_char g1, u_char b1, u_char a1 )
{
	SetOutColorRGBA( r0, g0, b0, a0, r1, g1, b1, a1,
					r1, g1, b1, a1, r1, g1, b1, a1 );
}
//==================================================================
void SetOutColorRGBA( u_char r0, u_char g0, u_char b0, u_char a0,
				   u_char r1, u_char g1, u_char b1, u_char a1,
				   u_char r2, u_char g2, u_char b2, u_char a2 )
{
	SetOutColorRGBA( r0, g0, b0, a0, r1, g1, b1, a1, r2, g2, b2, a2,
					r2, g2, b2, a2 );
}

//==================================================================
void SetOutColorRGBA( const u_char *rgba0p,
				   const u_char *rgba1p,
				   const u_char *rgba2p,
				   const u_char *rgba3p )
{
const	u_char	*listp[4] = { rgba0p, rgba1p, rgba2p, rgba3p };
static u_char	basecol[] = { 255, 255, 255, 255 };
const	u_char	*p = basecol;

	u_char	(*colp)[4] = _ctxp->_sys_state._outcolverts;
	for (int i=0; i < 4; ++i)
	{
		if ( listp[i] )
			p = listp[i];

		colp[i][0] = p[0];
		colp[i][1] = p[1];
		colp[i][2] = p[2];
		colp[i][3] = p[3];
	}
}
//==================================================================
void SetOutColorRGB( u_char r0, u_char g0, u_char b0 )
{
	SetOutColorRGBA( r0, g0, b0, 255 );
}
//==================================================================
void SetOutColorRGB( u_char r0, u_char g0, u_char b0,
				  u_char r1, u_char g1, u_char b1 )
{
	SetOutColorRGBA( r0, g0, b0, 255, r1, g1, b1, 255 );
}
//==================================================================
void SetOutColorRGB( u_char r0, u_char g0, u_char b0,
				  u_char r1, u_char g1, u_char b1,
				  u_char r2, u_char g2, u_char b2 )
{
	SetOutColorRGBA( r0, g0, b0, 255, r1, g1, b1, 255, r2, g2, b2, 255 );
}
//==================================================================
void SetOutColorRGB( u_char r0, u_char g0, u_char b0,
				  u_char r1, u_char g1, u_char b1,
				  u_char r2, u_char g2, u_char b2,
				  u_char r3, u_char g3, u_char b3 )
{
	SetOutColorRGBA( r0, g0, b0, 255, r1, g1, b1, 255, r2, g2, b2, 255, r3, g3, b3, 255 );
}

//==================================================================
void SetOutColorRGB( const u_char *rgb0p,
				  const u_char *rgb1p,
				  const u_char *rgb2p,
				  const u_char *rgb3p )
{
const	u_char	*listp[4] = { rgb0p, rgb1p, rgb2p, rgb3p };
static u_char	basecol[] = { 255, 255, 255, 255 };
const	u_char	*p = basecol;

	u_char	(*colp)[4] = _ctxp->_sys_state._outcolverts;
	for (int i=0; i < 4; ++i)
	{
		if ( listp[i] )
			p = listp[i];

		colp[i][0] = p[0];
		colp[i][1] = p[1];
		colp[i][2] = p[2];
		colp[i][3] = 255;
	}
}

//============================================================================
__inline static void vertex_rot( float x, float y, float cx, float cy, float co, float si )
{
	float rx = x * co - y * si + cx;
	float ry = x * si + y * co + cy;

	SetVertPos( rx, ry );
}

//============================================================================
void DrawSpriteCalcSize( Sprite *spritep, float *wp, float *hp, float size_sca_x, float size_sca_y )
{
	if ( wp )	*wp = spritep->_w * _ctxp->_sys_state._sca_x * size_sca_x;
	if ( hp )	*hp = spritep->_h * _ctxp->_sys_state._sca_y * size_sca_y;
}

//============================================================================
void DrawSprite( Sprite *spritep, float x, float y, float size_sca_x, float size_sca_y )
{
	float w = spritep->_w * size_sca_x;
	float h = spritep->_h * size_sca_y;

	DrawSpriteRect( spritep, x, y, w, h );
}

//============================================================================
void DrawSpriteRect( Sprite *spritep, float x, float y, float w, float h )
{
#ifdef SPRITE_INT_COORDS
	x = floorf( x );
	y = floorf( y );
#endif

	SelectTexture( spritep->GetTexture() );
	SetBlend( spritep->GetBasicBlend() );
	PrimBegin( PRIM_QUADS );
		if ( _ctxp->_sys_state._rot_z_cos == 1 )
		{
			w = w * _ctxp->_sys_state._sca_x;
			h = h * _ctxp->_sys_state._sca_y;
			float xx = x + spritep->_odx * _ctxp->_sys_state._sca_x;
			float yy = y + spritep->_ody * _ctxp->_sys_state._sca_y;
			SetVertRGBA( _ctxp->_sys_state._colverts[0] ); SetVertTxcoord( spritep->_s1, spritep->_t1 ); SetVertPos( xx, yy );
			SetVertRGBA( _ctxp->_sys_state._colverts[1] ); SetVertTxcoord( spritep->_s2, spritep->_t1 ); SetVertPos( xx+w, yy );
			SetVertRGBA( _ctxp->_sys_state._colverts[2] ); SetVertTxcoord( spritep->_s2, spritep->_t2 ); SetVertPos( xx+w, yy+h );
			SetVertRGBA( _ctxp->_sys_state._colverts[3] ); SetVertTxcoord( spritep->_s1, spritep->_t2 ); SetVertPos( xx, yy+h );
		}
		else
		{
			float dw = w * _ctxp->_sys_state._sca_x * 0.5f;
			float dh = h * _ctxp->_sys_state._sca_y * 0.5f;
			float xx = x + spritep->_odx * _ctxp->_sys_state._sca_x + dw;
			float yy = y + spritep->_ody * _ctxp->_sys_state._sca_y + dh;
			float cosa = _ctxp->_sys_state._rot_z_cos;
			float sina = _ctxp->_sys_state._rot_z_sin;
			SetVertRGBA( _ctxp->_sys_state._colverts[0] ); SetVertTxcoord( spritep->_s1, spritep->_t1 ); vertex_rot( -dw, -dh, xx, yy, cosa, sina );
			SetVertRGBA( _ctxp->_sys_state._colverts[1] ); SetVertTxcoord( spritep->_s2, spritep->_t1 ); vertex_rot(  dw, -dh, xx, yy, cosa, sina );
			SetVertRGBA( _ctxp->_sys_state._colverts[2] ); SetVertTxcoord( spritep->_s2, spritep->_t2 ); vertex_rot(  dw,  dh, xx, yy, cosa, sina );
			SetVertRGBA( _ctxp->_sys_state._colverts[3] ); SetVertTxcoord( spritep->_s1, spritep->_t2 ); vertex_rot( -dw,  dh, xx, yy, cosa, sina );
		}
	PrimEnd();
}

//============================================================================
void DrawSpriteRepeat( Sprite *spritep, float x, float y, float w, float h, float size_sca_x, float size_sca_y )
{
#ifdef SPRITE_INT_COORDS
	x = floorf( x );
	y = floorf( y );
#endif

	float	x2 = x + w;
	float	y2 = y + h;

	float	sca_odx = spritep->_odx * _ctxp->_sys_state._sca_x * size_sca_x;
	float	sca_ody = spritep->_ody * _ctxp->_sys_state._sca_y * size_sca_y;

	SelectTexture( spritep->GetTexture() );
	PrimBegin( PRIM_QUADS );

	float sph = spritep->_h * _ctxp->_sys_state._sca_y * size_sca_y;
	for (float ycnt=y; ycnt < y2; ycnt += sph)
	{
		float yy = ycnt + sca_ody;

		if ( ycnt + sph > y2 )
			sph = y2 - ycnt;

		float	yy2 = yy + sph;

		float spw = spritep->_w * _ctxp->_sys_state._sca_x * size_sca_x;

		for (float xcnt=x; xcnt < x2; xcnt += spw)
		{
			float xx = xcnt + sca_odx;

			if ( xcnt + spw > x2 )
				spw = x2 - xcnt;

			SetVertRGBA( _ctxp->_sys_state._colverts[0] ); SetVertTxcoord( spritep->_s1, spritep->_t1 ); SetVertPos( xx, yy );
			SetVertRGBA( _ctxp->_sys_state._colverts[1] ); SetVertTxcoord( spritep->_s2, spritep->_t1 ); SetVertPos( xx+spw, yy );
			SetVertRGBA( _ctxp->_sys_state._colverts[2] ); SetVertTxcoord( spritep->_s2, spritep->_t2 ); SetVertPos( xx+spw, yy2 );
			SetVertRGBA( _ctxp->_sys_state._colverts[3] ); SetVertTxcoord( spritep->_s1, spritep->_t2 ); SetVertPos( xx, yy2 );
		}
	}

	PrimEnd();
}

//==================================================================
static void draw_fan( float cx, float cy, float radius,
					  float ang_start,
					  float ang_end,
					  int ndiv,
					  const u_char incol[4],
					  const u_char outcol_sta[4],
					  const u_char outcol_end[4] )
{
	ang_start *= 3.14159265 / 180;
	ang_end *= 3.14159265 / 180;

	float	ang_step = (ang_end - ang_start) / ndiv;

	PrimBegin( PRIM_TRIFAN );

		SetVertRGBA( incol );
		SetVertPos( cx, cy );

		for (int i=0; i <= ndiv; ++i)
		{
			float	co = cosf( ang_start + i * ang_step );
			float	si = sinf( ang_start + i * ang_step );

			u_char	outcol[4];

			for (int j=0; j < 4; ++j)
				outcol[j] = ((int)outcol_sta[j] * (ndiv-i) + (int)outcol_end[j] * i) / ndiv;

			SetVertRGBA( outcol );
			SetVertPos( cx + radius * co, cy - radius * si );
		}

	PrimEnd();
}

//==================================================================
static void drawCorner( float cx, float cy,
						 float radius,
						 float thickness,
						 float ang_start,
						 float ang_end,
						 int ndiv,
						 const u_char incol[4],
						 const u_char midcol[4],
						 const u_char outcol[4] )
{
	ang_start *= 3.14159265f / 180;
	ang_end *= 3.14159265f / 180;

	float	ang_step = (ang_end - ang_start) / ndiv;

	PrimBegin( PRIM_QUADS );

		float	old_x0 = 0;
		float	old_y0 = 0;
		float	old_x1 = 0;
		float	old_y1 = 0;
		float	old_x2 = 0;
		float	old_y2 = 0;

		for (int i=0; i <= ndiv; ++i)
		{
			float	co = cosf( ang_start + i * ang_step );
			float	si = sinf( ang_start + i * ang_step );

			float	x0 = cx + co *  (radius - thickness*0.5f);
			float	y0 = cy + si * -(radius - thickness*0.5f);
			float	x1 = cx + co *  (radius);
			float	y1 = cy + si * -(radius);
			float	x2 = cx + co *  (radius + thickness*0.5f);
			float	y2 = cy + si * -(radius + thickness*0.5f);

			if ( i > 0 )
			{
				SetVertRGBA( incol );	SetVertPos( x0, y0 );
				SetVertRGBA( midcol );	SetVertPos( x1, y1 );
				SetVertRGBA( midcol );	SetVertPos( old_x1, old_y1 );
				SetVertRGBA( incol );	SetVertPos( old_x0, old_y0 );

				SetVertRGBA( midcol );	SetVertPos( x1, y1 );
				SetVertRGBA( outcol );	SetVertPos( x2, y2 );
				SetVertRGBA( outcol );	SetVertPos( old_x2, old_y2 );
				SetVertRGBA( midcol );	SetVertPos( old_x1, old_y1 );
			}
			old_x0 = x0;
			old_y0 = y0;
			old_x1 = x1;
			old_y1 = y1;
			old_x2 = x2;
			old_y2 = y2;
		}

	PrimEnd();
}

//==================================================================
void DrawRoundRectFill( float x, float y, float w, float h, float radius,
							 bool hard1, bool hard2, bool hard3, bool hard4 )
{
	float	x1 = x;
	float	y1 = y;
	float	x2 = x+w;
	float	y2 = y+h;

	float	x1in = x + radius;
	float	y1in = y + radius;
	float	x2in = x+w - radius;
	float	y2in = y+h - radius;

	SelectTexture( 0 );

	// draw inside
	PrimBegin( PRIM_TRISTRIP );
		SetVertRGBA( _ctxp->_sys_state._colverts[0] ); SetVertPos( x1in, y1in );
		SetVertRGBA( _ctxp->_sys_state._colverts[1] ); SetVertPos( x2in, y1in );
		SetVertRGBA( _ctxp->_sys_state._colverts[3] ); SetVertPos( x1in, y2in );
		SetVertRGBA( _ctxp->_sys_state._colverts[2] ); SetVertPos( x2in, y2in );
	PrimEnd();

	// Left
	PrimBegin( PRIM_QUADS );
		SetVertRGBA( _ctxp->_sys_state._outcolverts[0] );	SetVertPos( x1, y1in );
		SetVertRGBA( _ctxp->_sys_state._colverts[0] );	SetVertPos( x1in, y1in );
		SetVertRGBA( _ctxp->_sys_state._colverts[2] );	SetVertPos( x1in, y2in );
		SetVertRGBA( _ctxp->_sys_state._outcolverts[2] );	SetVertPos( x1, y2in );
//	PrimEnd();

	// Right
//	PrimBegin( PRIM_TRISTRIP );
		SetVertRGBA( _ctxp->_sys_state._colverts[1] );	SetVertPos( x2in, y1in );
		SetVertRGBA( _ctxp->_sys_state._outcolverts[1] ); SetVertPos( x2, y1in );
		SetVertRGBA( _ctxp->_sys_state._outcolverts[3] ); SetVertPos( x2, y2in );
		SetVertRGBA( _ctxp->_sys_state._colverts[3] );	SetVertPos( x2in, y2in );
	PrimEnd();

	// Top
	PrimBegin( PRIM_TRISTRIP );
		SetVertRGBA( _ctxp->_sys_state._outcolverts[0] ); SetVertPos( x1in, y1 );
		SetVertRGBA( _ctxp->_sys_state._outcolverts[1] ); SetVertPos( x2in, y1 );

		SetVertRGBA( _ctxp->_sys_state._colverts[0] ); SetVertPos( x1in, y1in );
		SetVertRGBA( _ctxp->_sys_state._colverts[1] ); SetVertPos( x2in, y1in );
	PrimEnd();

	// Bottom
	PrimBegin( PRIM_TRISTRIP );
		SetVertRGBA( _ctxp->_sys_state._colverts[2] ); SetVertPos( x1in, y2in );
		SetVertRGBA( _ctxp->_sys_state._colverts[3] ); SetVertPos( x2in, y2in );

		SetVertRGBA( _ctxp->_sys_state._outcolverts[2] ); SetVertPos( x1in, y2 );
		SetVertRGBA( _ctxp->_sys_state._outcolverts[3] ); SetVertPos( x2in, y2 );
	PrimEnd();

	if ( hard1 )
	{
		PrimBegin( PRIM_TRISTRIP );
			SetVertRGBA( _ctxp->_sys_state._colverts[0] ); SetVertPos( x1, y1 );
			SetVertRGBA( _ctxp->_sys_state._colverts[0] ); SetVertPos( x1in, y1 );
			SetVertRGBA( _ctxp->_sys_state._colverts[0] ); SetVertPos( x1,  y1in );
			SetVertRGBA( _ctxp->_sys_state._colverts[0] ); SetVertPos( x1in, y1in );
		PrimEnd();
	}
	else
		draw_fan( x1in, y1in, radius, 180, 90,  4, _ctxp->_sys_state._colverts[0], _ctxp->_sys_state._outcolverts[0], _ctxp->_sys_state._outcolverts[0] );

	if ( hard2 )
	{
		PrimBegin( PRIM_TRISTRIP );
			SetVertRGBA( _ctxp->_sys_state._colverts[1] ); SetVertPos( x2, y1 );
			SetVertRGBA( _ctxp->_sys_state._colverts[1] ); SetVertPos( x2in, y1 );
			SetVertRGBA( _ctxp->_sys_state._colverts[1] ); SetVertPos( x2,  y1in );
			SetVertRGBA( _ctxp->_sys_state._colverts[1] ); SetVertPos( x2in, y1in );
		PrimEnd();
	}
	else
		draw_fan( x2in, y1in, radius, 90,	 0,	 4, _ctxp->_sys_state._colverts[1], _ctxp->_sys_state._outcolverts[1], _ctxp->_sys_state._outcolverts[1] );

	if ( hard4 )
	{
		PrimBegin( PRIM_TRISTRIP );
			SetVertRGBA( _ctxp->_sys_state._colverts[3] ); SetVertPos( x1, y2 );
			SetVertRGBA( _ctxp->_sys_state._colverts[3] ); SetVertPos( x1in, y2 );
			SetVertRGBA( _ctxp->_sys_state._colverts[3] ); SetVertPos( x1,  y2in );
			SetVertRGBA( _ctxp->_sys_state._colverts[3] ); SetVertPos( x1in, y2in );
		PrimEnd();
	}
	else
		draw_fan( x1in, y2in, radius, 180, 270, 4, _ctxp->_sys_state._colverts[3], _ctxp->_sys_state._outcolverts[3], _ctxp->_sys_state._outcolverts[3] );

	if ( hard3 )
	{
		PrimBegin( PRIM_TRISTRIP );
			SetVertRGBA( _ctxp->_sys_state._colverts[2] ); SetVertPos( x2, y2 );
			SetVertRGBA( _ctxp->_sys_state._colverts[2] ); SetVertPos( x2in, y2 );
			SetVertRGBA( _ctxp->_sys_state._colverts[2] ); SetVertPos( x2,  y2in );
			SetVertRGBA( _ctxp->_sys_state._colverts[2] ); SetVertPos( x2in, y2in );
		PrimEnd();
	}
	else
		draw_fan( x2in, y2in, radius, 270, 360, 4, _ctxp->_sys_state._colverts[2], _ctxp->_sys_state._outcolverts[2], _ctxp->_sys_state._outcolverts[2] );
}

//============================================================================
void DrawRectFill( float x, float y, float w, float h )
{
	float x2 = x + w;
	float y2 = y + h;

	SelectTexture( 0 );
	PrimBegin( PRIM_QUADS );
		SetVertRGBA( _ctxp->_sys_state._colverts[0] ); SetVertPos( x, y );
		SetVertRGBA( _ctxp->_sys_state._colverts[1] ); SetVertPos( x2, y );
		SetVertRGBA( _ctxp->_sys_state._colverts[2] ); SetVertPos( x2, y2 );
		SetVertRGBA( _ctxp->_sys_state._colverts[3] ); SetVertPos( x, y2 );
	PrimEnd();
}

//==================================================================
static void draw_tube_h( float x, float y, float w, float thickness,
					     const u_char col0[4], const u_char col1[4], const u_char col2[4] )
{
	PrimBegin( PRIM_QUADS );

	float	x0 = x;
	float	x1 = x + w;
	float	y0 = y-thickness*0.5f;
	float	y1 = y;
	float	y2 = y+thickness*0.5f;

	SetVertRGBA( col0 );	SetVertPos( x0, y0 );
	SetVertRGBA( col0 );	SetVertPos( x1, y0 );
	SetVertRGBA( col1 );	SetVertPos( x1, y1 );
	SetVertRGBA( col1 );	SetVertPos( x0, y1 );

	SetVertRGBA( col1 );	SetVertPos( x0, y1 );
	SetVertRGBA( col1 );	SetVertPos( x1, y1 );
	SetVertRGBA( col2 );	SetVertPos( x1, y2 );
	SetVertRGBA( col2 );	SetVertPos( x0, y2 );

	PrimEnd();
}

//==================================================================
static void draw_tube_v( float x, float y, float h, float thickness,
					     const u_char col0[4], const u_char col1[4], const u_char col2[4] )
{
	PrimBegin( PRIM_QUADS );

	float	x0 = x-thickness*0.5f;
	float	x1 = x;
	float	x2 = x+thickness*0.5f;
	float	y0 = y;
	float	y1 = y + h;

	SetVertRGBA( col1 );	SetVertPos( x1, y0 );
	SetVertRGBA( col1 );	SetVertPos( x1, y1 );
	SetVertRGBA( col0 );	SetVertPos( x0, y1 );
	SetVertRGBA( col0 );	SetVertPos( x0, y0 );

	SetVertRGBA( col1 );	SetVertPos( x1, y0 );
	SetVertRGBA( col1 );	SetVertPos( x1, y1 );
	SetVertRGBA( col2  );	SetVertPos( x2, y1 );
	SetVertRGBA( col2  );	SetVertPos( x2, y0 );

	PrimEnd();
}

//==================================================================
void DrawRoundRectFrame( float x, float y, float w, float h, float radius, float thickness,
							  const u_char incol[4], const u_char midcol[4], const u_char outcol[4],
							  u_int mask )
{
	SelectTexture( 0 );

	float	tk	= 0;//thickness;
	float	htk = 0;//thickness * 0.5f;

	// Left
	if ( mask & FRAME_MASK_LEFT )	
		draw_tube_v( x+0*thickness*0.5f, y+htk+radius, h-tk-2*radius, thickness, outcol, midcol, incol );
	
	// Right
	if ( mask & FRAME_MASK_RIGHT )	
		draw_tube_v( x+w-0*thickness*0.5f, y+htk+radius, h-tk-2*radius, thickness, incol, midcol, outcol );
	
	// Top
	if ( mask & FRAME_MASK_TOP )
		draw_tube_h( x+htk+radius, y+0*thickness*0.5f, w-tk-2*radius, thickness, outcol, midcol, incol );
	
	// Bottom
	if ( mask & FRAME_MASK_BOTTOM )	
		draw_tube_h( x+htk+radius, y+h-0*thickness*0.5f, w-tk-2*radius, thickness, incol, midcol, outcol );

	bool	hard1 = !((mask & FRAME_MASK_LEFT  ) && (mask & FRAME_MASK_TOP));
	bool	hard2 = !((mask & FRAME_MASK_RIGHT ) && (mask & FRAME_MASK_TOP));
	bool	hard3 = !((mask & FRAME_MASK_RIGHT ) && (mask & FRAME_MASK_BOTTOM));
	bool	hard4 = !((mask & FRAME_MASK_LEFT  ) && (mask & FRAME_MASK_BOTTOM));

	float	x1in = x + radius + thickness*0.35f;// + htk;
	float	y1in = y + radius + thickness*0.35f;// + htk;
	float	x2in = x + w - radius - thickness*0.35f;// - htk;
	float	y2in = y + h - radius - thickness*0.35f;// - htk;

	if NOT( hard1 )
		drawCorner( x1in, y1in, radius, thickness, 180, 90,  4, incol, midcol, outcol );

	if NOT( hard2 )
		drawCorner( x2in, y1in, radius, thickness, 90,   0,  4, incol, midcol, outcol );

	if NOT( hard4 )
		drawCorner( x1in, y2in, radius, thickness, 180, 270, 4, incol, midcol, outcol );

	if NOT( hard3 )
		drawCorner( x2in, y2in, radius, thickness, 270, 360, 4, incol, midcol, outcol );
}

//============================================================================
void DrawRectFrame( float x, float y, float w, float h, float tickness, u_int mask )
{
	if ( mask & FRAME_MASK_TOP )	DrawRectFill( x, y,			w, tickness );
	if ( mask & FRAME_MASK_BOTTOM )	DrawRectFill( x, y+h-tickness, w, tickness );

	float	yy = y+tickness;
	float	hh = h-tickness*2;

	if ( mask & FRAME_MASK_LEFT )	DrawRectFill( x,			 yy, tickness, hh );
	if ( mask & FRAME_MASK_RIGHT )	DrawRectFill( x+w-tickness, yy, tickness, hh );
}

//============================================================================
void DrawLine( float x1, float y1, float x2, float y2 )
{
	SelectTexture( 0 );
	PrimBegin( PRIM_LINES );
		SetVertRGBA( _ctxp->_sys_state._colverts[0] ); SetVertPos( x1, y1 );
		SetVertRGBA( _ctxp->_sys_state._colverts[1] ); SetVertPos( x2, y2 );
	PrimEnd();
}
//============================================================================
void DrawLine( const float xy1[2], const float xy2[2] )
{
	SelectTexture( 0 );
	PrimBegin( PRIM_LINES );
		SetVertRGBA( _ctxp->_sys_state._colverts[0] ); SetVertPos( xy1 );
		SetVertRGBA( _ctxp->_sys_state._colverts[1] ); SetVertPos( xy2 );
	PrimEnd();
}

//==================================================================
};
